/**
  ******************************************************************************
  * @file    TIM/OCActive/main.c 
  * @author  MCD Application Team
  * @version V3.1.2
  * @date    09/28/2009
  * @brief   Main program body
  ******************************************************************************
  * @copy
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2009 STMicroelectronics</center></h2>
  */ 

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "string.h"
#include "Apptimer.h"
/** @addtogroup STM32F10x_StdPeriph_Examples
  * @{
  */

/** @addtogroup TIM_OCActive
  * @{
  */ 

/* Private typedef -----------------------------------------------------------*/

typedef struct {
uint16_t Width;  // pulse high width
uint16_t Period;   
uint16_t TimeStamp0;  // 0->1 time stamp
uint16_t state;
uint16_t Valid;
uint16_t  Timer;
}PWM_IN;

extern "C" {
  void TIM2_IRQHandler(void);  
  void TIM3_IRQHandler(void);  
};
/* Private define ------------------------------------------------------------*/

#define     MAX_PWM_PERIOD              30000              // 30 ms
#define     MIN_PWM_PERIOD              15000              // 15 ms
#define     PWM_PERIOD_MAX_DEV          10000              // 200 us deviation
#define     PWM_MAX_WIDTH               2500               // 2500 us max width
#define     PWM_MIN_WIDTH               500                // 500 us ms min width

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/

#if 1

static PWM_IN  pwm_in[8];
static INT16U  max_period,min_period;

static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Enable the TIM3 global Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  NVIC_Init(&NVIC_InitStructure);  
}

void Pwm_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  TIM_OCInitTypeDef  TIM_OCInitStructure;
  TIM_ICInitTypeDef  TIM_ICInitStructure;
  
  /* PCLK1 = HCLK/4 */
  RCC_PCLK1Config(RCC_HCLK_Div4);
  RCC_PCLK2Config(RCC_HCLK_Div2);
  /* TIM1, TIM4 clock enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4|RCC_APB1Periph_TIM2|RCC_APB1Periph_TIM3, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); 
  
  /*GPIOB Configuration: TIM4 channel1, 2, 3 and 4 */
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOB, &GPIO_InitStructure);

  /*GPIOB Configuration: TIM1 channel 1 and 4 */
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_11 | GPIO_Pin_8;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOA, &GPIO_InitStructure);  
  /* Timer4 setup */
  /* Time base configuration 
    TIM4 CLK = 36 MHz
    TIM1 CLK = 72 MHz
  */
  /* Timer #1 Init */
  TIM_TimeBaseStructure.TIM_Period = 20000-1;  // 20 ms period
  TIM_TimeBaseStructure.TIM_Prescaler = 72-1;  // 1us resolution
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
  
  // **************  !!! *************
  // Beware to init the un-used fields of OCInitStruct before call OCxInit
  // otherwise expected output will be generated
  
  memset(&TIM_OCInitStructure,0,sizeof(TIM_OCInitStructure));
  
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 0;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
 
//  TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;

  TIM_OC1Init(TIM1, &TIM_OCInitStructure);
  TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
  
  TIM_OC4Init(TIM1, &TIM_OCInitStructure);
  TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable);

  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
  
  TIM_OC2Init(TIM1, &TIM_OCInitStructure);
  TIM_OC3Init(TIM1, &TIM_OCInitStructure);
  
  /* Timer 4 Init */
  TIM_TimeBaseStructure.TIM_Prescaler = 36-1;  // 1us resolution  
  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); 
  
  /* PWM1 Mode configuration: Channel1 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 0;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

  TIM_OC1Init(TIM4, &TIM_OCInitStructure);

  TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);

  /* PWM1 Mode configuration: Channel2 */

  TIM_OC2Init(TIM4, &TIM_OCInitStructure);

  TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);

  /* PWM1 Mode configuration: Channel3 */

  TIM_OC3Init(TIM4, &TIM_OCInitStructure);

  TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);

  /* PWM1 Mode configuration: Channel4 */

  TIM_OC4Init(TIM4, &TIM_OCInitStructure);

  TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);
  

  TIM_ARRPreloadConfig(TIM4, ENABLE);
  TIM_ARRPreloadConfig(TIM1, ENABLE);

  TIM_CtrlPWMOutputs(TIM1,ENABLE); 
  
  /* enable counter */
  TIM_Cmd(TIM4, ENABLE);
  TIM_Cmd(TIM1, ENABLE);
  
  // Capture Init
  // Timwe #2 setup
  NVIC_Configuration();
  
  // GPIO setup
#ifndef  CONFIG_ADC
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOB, &GPIO_InitStructure);
#endif
  
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_6|GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  //   Timer setup
  
  /* Timer #2, #3 Init */
  TIM_TimeBaseStructure.TIM_Period = 0xffff;  // 20 ms period
  TIM_TimeBaseStructure.TIM_Prescaler = 36-1;  // 1us resolution
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);  
  
  memset(&TIM_ICInitStructure,0,sizeof(TIM_ICInitStructure));
  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  TIM_ICInitStructure.TIM_ICFilter = 5;

  for(int i=0;i<4;i++){
    TIM_ICInitStructure.TIM_Channel = i*4;
    TIM_ICInit(TIM2, &TIM_ICInitStructure);
    if(i<2) TIM_ICInit(TIM3, &TIM_ICInitStructure);    
  }  
  TIM_Cmd(TIM2, ENABLE); 
  TIM_Cmd(TIM3, ENABLE);  
  memset(pwm_in,0,sizeof(pwm_in));
  
  TIM_ITConfig(TIM2,TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4,ENABLE);
#ifdef  CONFIG_ADC
  TIM_ITConfig(TIM3,TIM_IT_CC1|TIM_IT_CC2,ENABLE); // CC3, CC4 for ADC input
#else
  TIM_ITConfig(TIM3,TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4,ENABLE);
#endif

  max_period = MAX_PWM_PERIOD ;
  min_period = MIN_PWM_PERIOD ;
}


/*
  Channel - 0 ~ 5
  Output - 0 ~ 19999 (0~19999 us) pulse high
  Cycle time = 20 ms (20000us)
  
*/
static const __IO uint16_t *CCR1[]={&TIM1->CCR1,&TIM1->CCR4,&TIM4->CCR1,&TIM4->CCR2,&TIM4->CCR3,&TIM4->CCR4};
static const __IO uint16_t *EGR[]={&TIM1->EGR,&TIM1->EGR,&TIM4->EGR,&TIM4->EGR,&TIM4->EGR,&TIM4->EGR};
static const uint16_t EGR_MASK[]={1<<1,1<<2,1<<1,1<<2,1<<3,1<<4};

void Pwm_Output(int Channel, int Output)
{
  uint16_t ccr_tmp; 

  ccr_tmp = *((__IO uint16_t *)CCR1[Channel]);
  *((__IO uint16_t *)CCR1[Channel]) = Output;
  if(Output && (ccr_tmp == 0)){
      *((__IO uint16_t *)EGR[Channel]) |= EGR_MASK[Channel];
  }
}

int Pwm_Input(int Channel)
{
  PWM_IN *p;
  p = &pwm_in[Channel];
  if(p->Valid){
    p->Valid = 0;
    p->Timer = SetTimer(200); // 200 ms time out
    return p->Width;
  }
  if(ChkTimer(p->Timer) == 0){
    p->Timer = SetTimer(0);
    return -1;
  }
  return p->Width;
}

int Pwm_Period_Chk(int Channel)
{
  PWM_IN *p;
  INT16U period;
  p = &pwm_in[Channel];
  period = p->Period;
  if(period > max_period) return 0;
  if(period < min_period) return 0;
  return 1;
}


void TIM2_IRQHandler(void)
{
  uint16_t IntStatus,TimeStamp,Width ;
  IntStatus = TIM2->SR;
  if(IntStatus & TIM_IT_CC1){ // CC1 capture interrupt
    TimeStamp = TIM2->CCR1;
    switch (pwm_in[0].state){       
     case 1: // rising edge detect        
        Width = TimeStamp-pwm_in[0].TimeStamp0;
        if(Width > max_period) break;
        if(Width < min_period) break;
        pwm_in[0].Period = Width;
     case 0:  // first rising edge      
        pwm_in[0].TimeStamp0 = TimeStamp;
        TIM2->CCER |= 2;  // set to falling edge
        pwm_in[0].state = 2;
        break;
     case 2: // falling edge detect
        Width = TimeStamp-pwm_in[0].TimeStamp0;
        if(Width < PWM_MIN_WIDTH) break;
          if(Width <= PWM_MAX_WIDTH){
          pwm_in[0].Width = Width;
          pwm_in[0].Valid = 1;        
        } 
        TIM2->CCER &= ~2;  // set to rising edge
        pwm_in[0].state = 1;          
        break; 
    }   
    TIM2->SR = (uint16_t)~TIM_IT_CC1; // clear CC1
  }
  if(IntStatus & TIM_IT_CC2){ // CC2 capture interrupt
    TimeStamp = TIM2->CCR2;
    switch (pwm_in[1].state){       
     case 1: // rising edge detect        
        Width = TimeStamp-pwm_in[1].TimeStamp0;
        if(Width > max_period) break;
        if(Width < min_period) break;
        pwm_in[1].Period = Width;        
     case 0:  // first rising edge      
        pwm_in[1].TimeStamp0 = TimeStamp;
        TIM2->CCER |= 0x20;  // set to falling edge
        pwm_in[1].state = 2;
        break;
     case 2: // falling edge detect
        Width = TimeStamp-pwm_in[1].TimeStamp0;
        if(Width < PWM_MIN_WIDTH) break;
        if(Width <= PWM_MAX_WIDTH){
          pwm_in[1].Width = Width;
          pwm_in[1].Valid = 1;        
        } 
        TIM2->CCER &= ~0x20;  // set to rising edge
        pwm_in[1].state = 1;          
        break; 
    }   
    TIM2->SR = (uint16_t)~TIM_IT_CC2; // clear CC1
  }
  if(IntStatus & TIM_IT_CC3){ // CC3 capture interrupt
    TimeStamp = TIM2->CCR3;
    switch (pwm_in[2].state){       
     case 1: // rising edge detect        
        Width = TimeStamp-pwm_in[2].TimeStamp0;
        if(Width > max_period) break;
        if(Width < min_period) break;
        pwm_in[2].Period = Width;        
     case 0:  // first rising edge      
        pwm_in[2].TimeStamp0 = TimeStamp;
        TIM2->CCER |= 0x200;  // set to falling edge
        pwm_in[2].state = 2;
        break;
     case 2: // falling edge detect
        Width = TimeStamp-pwm_in[2].TimeStamp0;
        if(Width < PWM_MIN_WIDTH) break;
        if(Width <= PWM_MAX_WIDTH){
          pwm_in[2].Width = Width;
          pwm_in[2].Valid = 1;        
        } 
        TIM2->CCER &= ~0x200;  // set to rising edge
        pwm_in[2].state = 1;          
        break; 
    }   
    TIM2->SR = (uint16_t)~TIM_IT_CC3; // clear CC1
  }
  if(IntStatus & TIM_IT_CC4){ // CC4 capture interrupt
    TimeStamp = TIM2->CCR4;
    switch (pwm_in[3].state){       
     case 1: // rising edge detect        
        Width = TimeStamp-pwm_in[3].TimeStamp0;
        if(Width > max_period) break;
        if(Width < min_period) break;
        pwm_in[3].Period = Width;        
     case 0:  // first rising edge      
        pwm_in[3].TimeStamp0 = TimeStamp;
        TIM2->CCER |= 0x2000;  // set to falling edge
        pwm_in[3].state = 2;
        break;
     case 2: // falling edge detect
        Width = TimeStamp-pwm_in[3].TimeStamp0;
        if(Width < PWM_MIN_WIDTH) break;
        if(Width <= PWM_MAX_WIDTH){
          pwm_in[3].Width = Width;
          pwm_in[3].Valid = 1;        
        } 
        TIM2->CCER &= ~0x2000;  // set to rising edge
        pwm_in[3].state = 1;          
        break; 
    }   
    TIM2->SR = (uint16_t)~TIM_IT_CC4; // clear CC1
  }  
}

void TIM3_IRQHandler(void)
{
  uint16_t IntStatus,TimeStamp,Width ;
  IntStatus = TIM3->SR;
  if(IntStatus & TIM_IT_CC1){ // CC1 capture interrupt
    TimeStamp = TIM3->CCR1;
    switch (pwm_in[4].state){       
     case 1: // rising edge detect        
        Width = TimeStamp-pwm_in[4].TimeStamp0;
        if(Width > max_period) break;
        if(Width < min_period) break;
        pwm_in[4].Period = Width;        
     case 0:  // first rising edge      
        pwm_in[4].TimeStamp0 = TimeStamp;
        TIM3->CCER |= 2;  // set to falling edge
        pwm_in[4].state = 2;
        break;
     case 2: // falling edge detect
        Width = TimeStamp-pwm_in[4].TimeStamp0;
        if(Width < PWM_MIN_WIDTH) break;
        if(Width <= PWM_MAX_WIDTH){
          pwm_in[4].Width = Width;
          pwm_in[4].Valid = 1;        
        } 
        TIM3->CCER &= ~2;  // set to rising edge
        pwm_in[4].state = 1;          
        break; 
    }   
    TIM3->SR = (uint16_t)~TIM_IT_CC1; // clear CC1
  }
  if(IntStatus & TIM_IT_CC2){ // CC1 capture interrupt
    TimeStamp = TIM3->CCR2;
    switch (pwm_in[5].state){       
     case 1: // rising edge detect        
        Width = TimeStamp-pwm_in[5].TimeStamp0;
        pwm_in[5].Period = Width;        
     case 0:  // first rising edge      
        pwm_in[5].TimeStamp0 = TimeStamp;
        TIM3->CCER |= 0x20;  // set to falling edge
        pwm_in[5].state = 2;
        break;
     case 2: // falling edge detect
        Width = TimeStamp-pwm_in[5].TimeStamp0;
        if((Width > PWM_MIN_WIDTH) &&(Width <= PWM_MAX_WIDTH)){
            pwm_in[5].Width = Width;
            pwm_in[5].Valid = 1; 
        } 
        TIM3->CCER &= ~0x20;  // set to rising edge
        pwm_in[5].state = 1;          
        break; 
    }   
    TIM3->SR = (uint16_t)~TIM_IT_CC2; // clear CC1
  }
#ifndef CONFIG_ADC  
  if(IntStatus & TIM_IT_CC3){ // CC1 capture interrupt
    TimeStamp = TIM3->CCR3;
    switch (pwm_in[6].state){       
     case 1: // rising edge detect        
        Width = TimeStamp-pwm_in[6].TimeStamp0;
        if(Width > max_period) break;
        if(Width < min_period) break;
        pwm_in[6].Period = Width;        
     case 0:  // first rising edge      
        pwm_in[6].TimeStamp0 = TimeStamp;
        TIM3->CCER |= 0x200;  // set to falling edge
        pwm_in[6].state = 2;
        break;
     case 2: // falling edge detect
        Width = TimeStamp-pwm_in[6].TimeStamp0;
        if(Width < PWM_MIN_WIDTH) break;
        if(Width <= PWM_MAX_WIDTH){
          pwm_in[6].Width = Width;
          pwm_in[6].Valid = 1;        
        } 
        TIM3->CCER &= ~0x200;  // set to rising edge
        pwm_in[6].state = 1;          
        break; 
    }   
    TIM3->SR = (uint16_t)~TIM_IT_CC3; // clear CC1
  }
  if(IntStatus & TIM_IT_CC4){ // CC1 capture interrupt
    TimeStamp = TIM3->CCR4;
    switch (pwm_in[7].state){       
     case 1: // rising edge detect        
        Width = TimeStamp-pwm_in[7].TimeStamp0;
        if(Width > max_period) break;
        if(Width < min_period) break;
        pwm_in[7].Period = Width;        
     case 0:  // first rising edge      
        pwm_in[7].TimeStamp0 = TimeStamp;
        TIM3->CCER |= 0x2000;  // set to falling edge
        pwm_in[7].state = 2;
        break;
     case 2: // falling edge detect
        Width = TimeStamp-pwm_in[7].TimeStamp0;
        if(Width < PWM_MIN_WIDTH) break;
        if(Width <= PWM_MAX_WIDTH){
          pwm_in[7].Width = Width;
          pwm_in[7].Valid = 1;        
        } 
        TIM3->CCER &= ~0x2000;  // set to rising edge
        pwm_in[7].state = 1;          
        break; 
    }   
    TIM3->SR = (uint16_t)~TIM_IT_CC4; // clear CC1
  }
#endif  
}

#endif